#ifndef _TME_FILE_FMT_H_
#define _TME_FILE_FMT_H_

#include <stdio.h>
#include <stdint.h>

#ifdef __cplusplus
extern "C" {
#endif

/* 系统版本1.0 */
#define TME_VERSION 0x0100

/* 文件头魔数 */
#define TME_MAGIC_NUMBER ".TME"
/* 文件头大小 */
#define TME_HEAD_SIZE 512

/* FAT表大小 */
#define FAT_SIZE 512
/* FAT表单元个数 */
#define FAT_UNIT_NUMBER (FAT_SIZE / sizeof(uint32_t))
/* FAT表单元对应数据块大小 */
#define FAT_UNIT_BLOCK_SIZE 512

/* 属性表起始FAT单元序号 */
#define ATTR_TAB_FAT_INDEX 0
/* 属性表单元字节数 */
#define ATTR_TAB_UNIT_SIZE sizeof(TME_AttrTab)

/* 总览时间表起始FAT单元序号 */
#define TIME_TAB_FAT_INDEX 1

/* 存储所有属性名称的数据块起始FAT单元序号 */
#define ATTR_NAME_TAB_FAT_INDEX 2

/* -------------------- 文件头 -------------------- */

/* 文件头(2K) */
typedef struct TME_Head {
    uint32_t magicNumber; /* 魔数".TME" 2E 54 4D 45 排列,即数值: 0x454D542E */
    uint32_t fileSize; /* 文件总大小 */
    uint16_t version; /* 版本号,默认 TME_VERSION ,即数值 0x0100 */
    uint16_t headSize; /* 文件头大小,默认 TME_HEAD_SIZE */
    uint32_t reserve2;

    char descript[256]; /* 介绍 */

    uint32_t fatSize; /* 每个FAT表大小,默认 FAT_SIZE*/
    uint32_t fatUnitNumber; /* 每个FAT表单元个数,默认 FAT_UNIT_NUMBER */
    uint32_t fatUnitBlockSize; /* 每个FAT表单元对应数据块大小,默认 FAT_UNIT_BLOCK_SIZE */
    uint32_t fatCount; /* FAT表个数 */

    uint32_t attrFatIndex; /* 属性表起始FAT单元序号,默认 ATTR_TAB_FAT_INDEX */
    uint32_t attrUnitSize; /* 属性表中,每个属性信息占用字节数,默认 ATTR_TAB_UNIT_SIZE */
    uint32_t attrIdleId; /* 起始属性id,默认为1(从1数起) */
    uint32_t attrCount; /* 收录属性个数 */

    uint32_t timeFatIndex; /* 总览时间表起始FAT单元序号,默认 TIME_TAB_FAT_INDEX */
    uint32_t attrNameFatIndex; /* 存储所有属性名称的数据块起始FAT单元序号,默认 ATTR_NAME_TAB_FAT_INDEX */
    uint32_t reserve4;
    uint32_t reserve5;

    uint16_t beginYear; /* 文件/起始时间:年月日 */
    uint8_t beginMonth;
    uint8_t beginDay;
    uint32_t beginMs; /* 当日毫秒计数,例如时间 09:30:15.123 对应数值为 9*3600*1000 + 30*60*1000 + 15*1000 + 123 = 34215123 */
    uint32_t durationMs; /* 文件总时长: 毫秒 */
    uint32_t reserve6;

    uint32_t fatIdleIndex; /* 当前空闲fat序号起始 */
    uint32_t reserve7;
    uint32_t reserve8;
    uint32_t reserve9;
} TME_Head;

/* -------------------- fat表 -------------------- */

/* fat单元类型定义 */
typedef enum {
    TME_FAT_UNIT_TYPE_DATA = 0,
    TME_FAT_UNIT_TYPE_ATTR,
    TME_FAT_UNIT_TYPE_ATTR_NAME,
    TME_FAT_UNIT_TYPE_TIME,
} TME_FAT_UNIT_TYPE;

/* 读/写/删fat链表结束标志 */
#define GET_FAT_UNIT_END(fuv) (((fuv) & 0x80000000) ? 1 : 0)
#define SET_FAT_UNIT_END(fuv) (fuv |= 0x80000000)
#define DEL_FAT_UNIT_END(fuv) (fuv &= 0x7FFFFFFF)
/* 读/写/删fat链表开始标志 */
#define GET_FAT_UNIT_BEGIN(fuv) (((fuv) & 0x40000000) ? 1 : 0)
#define SET_FAT_UNIT_BEGIN(fuv) (fuv |= 0x40000000)
#define DEL_FAT_UNIT_BEGIN(fuv) (fuv &= 0xBFFFFFFF)
/* 读/写fat单元类型 */
#define GET_FAT_UNIT_TYPE(fuv) (((fuv) >> 28) & 0x03)
#define SET_FAT_UNIT_TYPE(fuv, type) fuv = ((fuv) & 0xCFFFFFFF) | (((type) & 0x03) << 28)
/* 读/写fat单元数值 */
#define GET_FAT_UNIT_VALUE(fuv) ((fuv) & 0x0FFFFFFF)
#define SET_FAT_UNIT_VALUE(fuv, val) fuv = ((fuv) & 0xF0000000) | ((val) & 0x0FFFFFFF)

/* -------------------- 属性表 -------------------- */

/* 属性表 */
typedef struct TME_AttrTab {
    uint32_t id; /* 属性ID,仅低28bit可用,自然累加计数生成 */
    uint32_t parentId; /* 父属性ID,仅低28bit可用(0表示没有父属性) */
    uint32_t dataFatIndex; /* 数据段的起始FAT序号(0表示还没有数据) */
    uint32_t timeFatIndex; /* 时间表的起始FAT序号(0表示还没有数据) */

    uint32_t nameLength; /* 属性名称长度N */
    uint32_t nameOffset; /* 属性名称在属性名称表中的偏移量 */
    uint32_t durationMs; /* 属性内容总时长,单位ms*/
    uint32_t dataSize; /* 属性内容总长度 */

    uint8_t shortName[32]; /* 预留空间(暂时用于属性名称前31字节,用于预览) */
} TME_AttrTab;

/* -------------------- 时间表 -------------------- */

/* 时间表单元类型定义 */
typedef enum {
    TME_TIME_TAB_UNIT_TYPE_DATA_DIFF = 0,
    TME_TIME_TAB_UNIT_TYPE_ATTR_DIFF,
    TME_TIME_TAB_UNIT_TYPE_OFFSET_DIFF,
    TME_TIME_TAB_UNIT_TYPE_TIME_DIFF = 8,
} TME_TIME_TAB_UNIT_TYPE;

/* 读/写时间表单元类型 */
#define GET_TIME_TAB_UNIT_TYPE_IS_DATA_DIFF(tuv) (((tuv >> 28) & 0x0F) == TME_TIME_TAB_UNIT_TYPE_DATA_DIFF)
#define GET_TIME_TAB_UNIT_TYPE_IS_ATTR_DIFF(tuv) (((tuv >> 28) & 0x0F) == TME_TIME_TAB_UNIT_TYPE_ATTR_DIFF)
#define GET_TIME_TAB_UNIT_TYPE_IS_OFFSET_DIFF(tuv) (((tuv >> 28) & 0x0F) == TME_TIME_TAB_UNIT_TYPE_OFFSET_DIFF)
#define GET_TIME_TAB_UNIT_TYPE_IS_TIME_DIFF(tuv) (((tuv >> 28) & 0x0F) >= TME_TIME_TAB_UNIT_TYPE_TIME_DIFF)
#define SET_TIME_TAB_UNIT_TYPE(tuv, type) tuv = (tuv & 0x0FFFFFFF) | (type << 28)
#define SET_TIME_TAB_UNIT_TYPE_TIME_DIFF(tuv) tuv |= 0x80000000
/* 读/写时间表单元数值 */
#define GET_TIME_TAB_UNIT_VALUE(tuv) (tuv & 0x0FFFFFFF)
#define SET_TIME_TAB_UNIT_VALUE(tuv, val) tuv |= val & 0x0FFFFFFF
#define GET_TIME_TAB_UNIT_VALUE_TIME_DIFF(tuv) (tuv & 0x7FFFFFFF)
#define SET_TIME_TAB_UNIT_VALUE_TIME_DIFF(tuv, val) tuv |= val & 0x7FFFFFFF

/* -------------------- file api -------------------- */

typedef struct TME_File {
    int32_t isReadOnly; /* 只读标志,在open时传入,每次调用mmap接口时使用 */
    int32_t isExtend; /* 这是续写模式 */
    uint32_t beginTickMs; /* 创建/打开文件时取得的系统ms值 */
    uint32_t diffTickMs; /* 打开文件时间和文件创建时间的差值,所以时间表中的增量值 = 当前TickMs - beginTickMs + diffTickMs */
    FILE* fp;
    TME_Head* head; /* 文件头映射内存 (映射文件内存) */
    uint32_t** fat; /* fat表映射内存数组,使用realloc扩张数组;长度信息在 head->fatCount (映射文件内存) */
} TME_File;

/*
 *  打开tme文件
 *  参数:
 *      file:文件名
 *      mode: 'r'只读,不存在返回失败 'w'存在则续写,否则创建 'c'存在则覆盖,否则创建
 *   返回:
 *      成功返回非NULL句柄指针
 */
TME_File* tme_file_open(const char* file, char mode);
void tme_file_close(TME_File* fp);

/* -------------------- util api -------------------- */

/* 获取当前和文件创建时间的差值ms */
uint32_t tme_file_getDiffMs(TME_File* fp);

/* -------------------- fat api -------------------- */

typedef struct TME_FatChain {
    TME_FAT_UNIT_TYPE type; /* fat链表数据类型 */
    int32_t totalSize; /* 数据总大小 */
    int32_t writeSeek; /* 写数据位置 */
    int32_t readSeek; /* 读数据位置 */
    int32_t fatBeginIndex; /* fat链表起始位置 */
    int32_t fatWriteIndex; /* 当前fat链表写位置 */
    int32_t fatReadIndex; /* 当前fat链表读位置 */
    int32_t writeBlockOffset; /* 正在占用的写内存块偏移地址 */
    int32_t readBlockOffset; /* 正在占用的读内存块偏移地址 */
    uint8_t* writeBlock; /* 正在占用的写内存块地址 (映射文件内存) */
    uint8_t* readBlock; /* 正在占用的读内存块地址 (映射文件内存) */
    TME_File* fp;
} TME_FatChain;

/* 取得fat链表,fatIndex=-1时自动创建新块 */
TME_FatChain* tme_fat_chain_open(TME_File* fp, int32_t fatIndex, TME_FAT_UNIT_TYPE type);
void tme_fat_chain_close(TME_FatChain* fc);
/* 往链表读/写数据,成功返回数据长度 */
int32_t tme_fat_chain_write(TME_FatChain* fc, const uint8_t* data, int32_t dataSize);
int32_t tme_fat_chain_read(TME_FatChain* fc, uint8_t* data, int32_t dataSize);
/* 移动读取位置,成功返回0(不影响写,写永远追加到后面) */
int32_t tme_fat_chain_readSeek(TME_FatChain* fc, int32_t offset);
int32_t tme_fat_chain_writeSeek(TME_FatChain* fc, int32_t offset);
/* 直接隐射数据块,要求offset+memSize必须在同一块block内,且内存范围已存在(映射write扩展后的范围) */
void* tme_fat_chain_mmap(TME_FatChain* fc, int32_t offset, int32_t memSize);
int32_t tme_fat_chain_unmmap(void* mem);

#ifdef __cplusplus
}
#endif

#endif // _TME_FILE_FMT_H_
